"""
Pytest配置文件
=============

全局测试配置、fixtures和工具函数
"""

import asyncio
import os
import tempfile
from typing import AsyncGenerator, Generator
from unittest.mock import MagicMock, Mock

import pytest
from httpx import AsyncClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import StaticPool

# pytest-html支持
try:
    from pytest_html import html
except ImportError:
    # 如果没有安装pytest-html，创建一个模拟的html对象
    class MockHTML:
        @staticmethod
        def th(text):
            return f"<th>{text}</th>"
        
        @staticmethod 
        def td(text):
            return f"<td>{text}</td>"
    
    html = MockHTML()

# 测试环境配置
os.environ.setdefault("ENVIRONMENT", "testing")
os.environ.setdefault("TESTING", "true")
os.environ.setdefault("DEBUG", "false")
os.environ.setdefault("REDFIRE_MYSQL_HOST", "localhost")
os.environ.setdefault("REDFIRE_MYSQL_PORT", "3307")
os.environ.setdefault("REDFIRE_MYSQL_USERNAME", "redfire_test")
os.environ.setdefault("REDFIRE_MYSQL_PASSWORD", "redfire_test_2025")
os.environ.setdefault("REDFIRE_MYSQL_DATABASE", "redfire_test")


@pytest.fixture(scope="session")
def event_loop():
    """创建事件循环"""
    loop = asyncio.get_event_loop_policy().new_event_loop()
    yield loop
    loop.close()


@pytest.fixture
def temp_dir() -> Generator[str, None, None]:
    """临时目录fixture"""
    with tempfile.TemporaryDirectory() as tmpdir:
        yield tmpdir


@pytest.fixture
def mock_config():
    """模拟配置对象"""
    config = Mock()
    config.environment = "testing"
    config.debug = False
    config.testing = True
    config.database_url = "sqlite:///test.db"
    config.redis_url = "redis://localhost:6379/1"
    config.jwt_secret_key = "test_secret_key"
    config.jwt_algorithm = "HS256"
    config.jwt_access_token_expire_minutes = 30
    config.jwt_refresh_token_expire_days = 7
    config.password_min_length = 8
    config.password_require_uppercase = True
    config.password_require_lowercase = True
    config.password_require_digits = True
    config.password_require_symbols = False
    config.max_login_attempts = 5
    config.lockout_duration_minutes = 30
    return config


@pytest.fixture
def mock_database_session():
    """模拟数据库会话"""
    session = Mock()
    session.commit = Mock()
    session.rollback = Mock()
    session.close = Mock()
    session.flush = Mock()
    session.refresh = Mock()
    return session


@pytest.fixture
def mock_redis():
    """模拟Redis客户端"""
    redis_client = Mock()
    redis_client.get = Mock(return_value=None)
    redis_client.set = Mock(return_value=True)
    redis_client.delete = Mock(return_value=1)
    redis_client.expire = Mock(return_value=True)
    redis_client.exists = Mock(return_value=False)
    return redis_client


@pytest.fixture
def sample_user_data():
    """示例用户数据"""
    return {
        "username": "testuser",
        "email": "test@example.com",
        "password": "TestPassword123",
        "full_name": "Test User",
        "phone": "13800138000",
        "is_active": True,
        "is_admin": False,
    }


@pytest.fixture
def sample_admin_data():
    """示例管理员数据"""
    return {
        "username": "admin",
        "email": "admin@example.com",
        "password": "AdminPassword123",
        "full_name": "Admin User",
        "phone": "13900139000",
        "is_active": True,
        "is_admin": True,
    }


@pytest.fixture
def mock_password_hash():
    """模拟密码哈希"""
    return "$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewdBPj/8s9BCRR4."


@pytest.fixture
def mock_jwt_token():
    """模拟JWT令牌"""
    return "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6InRlc3R1c2VyIiwiZXhwIjoxNjQwOTk1MjAwfQ.mock_signature"


@pytest.fixture
def mock_security_service():
    """模拟安全服务"""
    service = Mock()
    service.hash_password = Mock(return_value="$2b$12$test_hash")
    service.verify_password = Mock(return_value=True)
    service.create_access_token = Mock(return_value="mock_access_token")
    service.create_refresh_token = Mock(return_value="mock_refresh_token")
    service.verify_token = Mock(return_value={"user_id": 1, "username": "testuser"})
    service.validate_password_strength = Mock(return_value=True)
    return service


@pytest.fixture
def mock_validation_service():
    """模拟验证服务"""
    service = Mock()
    service.validate_email = Mock(return_value=True)
    service.validate_password = Mock(return_value=True)
    service.validate_phone = Mock(return_value=True)
    service.validate_username = Mock(return_value=True)
    service.validate_user_data = Mock(return_value=None)
    return service


@pytest.fixture
def mock_user_repository():
    """模拟用户仓储"""
    repo = Mock()
    repo.create = Mock()
    repo.get_by_id = Mock()
    repo.get_by_username = Mock()
    repo.get_by_email = Mock()
    repo.update = Mock()
    repo.delete = Mock()
    repo.list = Mock(return_value=[])
    repo.exists = Mock(return_value=False)
    return repo


@pytest.fixture
def mock_audit_service():
    """模拟审计服务"""
    service = Mock()
    service.log_user_action = Mock()
    service.log_security_event = Mock()
    service.log_error = Mock()
    return service


# 异步fixtures
@pytest.fixture
async def async_mock_database_session():
    """异步模拟数据库会话"""
    session = Mock()
    session.commit = Mock()
    session.rollback = Mock()
    session.close = Mock()
    session.flush = Mock()
    session.refresh = Mock()
    return session


# 测试数据库fixtures（如果需要真实数据库测试）
@pytest.fixture(scope="session")
def test_database_url():
    """测试数据库URL"""
    return "sqlite:///test_user_service.db"


# 清理fixtures
@pytest.fixture(autouse=True)
def cleanup_environment():
    """自动清理测试环境"""
    yield
    # 测试后清理工作
    # 清理临时文件、重置全局状态等


# 标记配置
def pytest_configure(config):
    """pytest配置钩子"""
    config.addinivalue_line("markers", "unit: 单元测试标记")
    config.addinivalue_line("markers", "integration: 集成测试标记")
    config.addinivalue_line("markers", "slow: 慢速测试标记")


# 测试收集钩子
def pytest_collection_modifyitems(config, items):
    """修改测试收集项"""
    for item in items:
        # 为单元测试添加标记
        if "unit" in str(item.fspath):
            item.add_marker(pytest.mark.unit)
        # 为集成测试添加标记
        elif "integration" in str(item.fspath):
            item.add_marker(pytest.mark.integration)
            item.add_marker(pytest.mark.slow)


# 测试报告钩子 - 新版本pytest-html支持
@pytest.hookimpl(optionalhook=True)
def pytest_html_results_table_header(cells):
    """自定义HTML报告表头"""
    cells.insert(2, html.th('Description'))


@pytest.hookimpl(optionalhook=True) 
def pytest_html_results_table_row(report, cells):
    """自定义HTML报告行"""
    cells.insert(2, html.td(report.description))


def pytest_configure(config):
    """配置pytest环境"""
    # 设置HTML报告标题
    if hasattr(config, '_metadata'):
        config._metadata['Project'] = '用户服务'
        config._metadata['Module'] = 'User Service Tests'


# 跳过条件
skip_if_no_redis = pytest.mark.skipif(not os.getenv("REDIS_URL"), reason="需要Redis连接")

skip_if_no_database = pytest.mark.skipif(
    not os.getenv("DATABASE_URL"), reason="需要数据库连接"
)

# ==================== 新增的集成测试fixtures ====================

@pytest.fixture(scope="session")
def test_app():
    """测试应用实例"""
    from app.main import create_app
    from app.containers import UserServiceContainer
    
    app = create_app()
    
    # 初始化测试容器
    container = UserServiceContainer()
    container.config.from_dict({
        "environment": "testing",
        "debug": False,
        "testing": True,
        "database_url": "sqlite:///test.db",
        "jwt_secret_key": "test_secret_key",
        "jwt_algorithm": "HS256",
        "jwt_access_token_expire_minutes": 30,
        "jwt_refresh_token_expire_days": 7,
    })
    
    # 配置测试容器 wiring
    container.wire(modules=[
        "app.api.v1.users",
        "app.api.v1.auth", 
        "app.services.user_service"
    ])
    
    app.container = container
    return app


@pytest.fixture
async def client(test_app):
    """异步HTTP客户端"""
    async with AsyncClient(app=test_app, base_url="http://test") as ac:
        yield ac


@pytest.fixture
def test_db_url():
    """测试数据库URL"""
    # 使用SQLite内存数据库用于测试
    return "sqlite:///:memory:"


@pytest.fixture
def test_engine(test_db_url):
    """测试数据库引擎"""
    engine = create_engine(
        test_db_url,
        connect_args={"check_same_thread": False},
        poolclass=StaticPool,
    )
    return engine


@pytest.fixture
def test_db_session(test_engine):
    """测试数据库会话"""
    TestingSessionLocal = sessionmaker(
        autocommit=False, autoflush=False, bind=test_engine
    )
    return TestingSessionLocal()


@pytest.fixture
def override_db_dependency(test_db_session):
    """重写数据库依赖"""
    from redfire_framework.core.database import create_database_manager
    
    def _override_get_db():
        return test_db_session
    
    return _override_get_db


@pytest.fixture
async def authenticated_client(client):
    """已认证的客户端"""
    # 创建测试用户并登录
    user_data = {
        "username": "testuser",
        "email": "test@example.com",
        "password": "TestPassword123!",
        "full_name": "Test User",
    }
    
    # 注册用户
    response = await client.post("/api/v1/users/", json=user_data)
    if response.status_code == 201:
        user_id = response.json()["data"]["id"]
        
        # 登录
        login_response = await client.post("/api/v1/auth/login", json={
            "username": user_data["username"],
            "password": user_data["password"]
        })
        
        if login_response.status_code == 200:
            token = login_response.json()["data"]["access_token"]
            client.headers.update({"Authorization": f"Bearer {token}"})
            client.user_data = user_data
            client.user_id = user_id
            client.token = token
    
    return client


@pytest.fixture
async def admin_client(client):
    """管理员客户端"""
    admin_data = {
        "username": "admin",
        "email": "admin@example.com", 
        "password": "AdminPassword123!",
        "full_name": "Admin User",
        "primary_role": "admin"
    }
    
    # 创建管理员用户
    response = await client.post("/api/v1/users/", json=admin_data)
    if response.status_code == 201:
        admin_id = response.json()["data"]["id"]
        
        # 登录
        login_response = await client.post("/api/v1/auth/login", json={
            "username": admin_data["username"],
            "password": admin_data["password"]
        })
        
        if login_response.status_code == 200:
            token = login_response.json()["data"]["access_token"]
            client.headers.update({"Authorization": f"Bearer {token}"})
            client.admin_data = admin_data
            client.admin_id = admin_id
            client.token = token
    
    return client


@pytest.fixture(autouse=True)
async def clean_database(test_db_session):
    """自动清理数据库"""
    yield
    # 测试后清理
    try:
        test_db_session.rollback()
        test_db_session.close()
    except Exception:
        pass
